package com.zsj.springbootmq.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

/**
 * @author https://gitee.com/zhengshengjun
 * @date 2023/11/17.
 */

@Configuration
@Slf4j
public class RabbitTemplateConfig {

    @Autowired
    RabbitTemplate rabbitTemplate;

    /**
     * 定制化rabbitmqTemplate
     * 设置确认回调setConfirmCallback
     * 消息正确抵达队列进行ack应答true
     */
    @PostConstruct //JsonRabbitMqConverter对象创建的时候初始化这个方法
    public void initRabbitTemplate() {

        /*
             void confirm(@Nullable CorrelationData correlationData, boolean ack, @Nullable String cause);
         */
        /**
         * correlationData(可空)  当前消息的唯一关联数据(消息的唯一id)
         * ack  消息是否成功收到
         * cause(可空)  消息失败的原因
         */
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            //消息应答和消息是否被消费没有关系 这个只是说消息进入了哪个队列而已
            log.info("消息应答机制confirm----correlationData:{},ack?:{},cause:{}", correlationData, ack, cause);
        });

        /*
       returnedMessage(Message message,
         int replyCode,
         String replyText,
          String exchange,
		String routingKey) {
         */

        /**
         * ReturnsCallback的构造方法中是一个ReturnedMessage方法
         * 新版中 这里得到一个ReturnedMessage对象 属性就是上面注释中的属性
         * @message 投递失败的消息详细信息
         * @replyCode 回复的状态码
         * @replyText 回复的文本信息
         * @exchange 当时发给哪个交换机
         * @routingKey 当时消息的路由键
         * 只要消息没有被投递给指定的队列,就触发这个失败回调
         */
        rabbitTemplate.setReturnsCallback((returnedMessage) -> {
            log.error("消息失败回调fail message:{} " +
                            "replyCode:{} " +
                            "replyText:{} " +
                            "exchange:{}" +
                            "routingKey:{} ",
                    returnedMessage.getMessage(),
                    returnedMessage.getReplyCode(),
                    returnedMessage.getReplyText(),
                    returnedMessage.getExchange(),
                    returnedMessage.getRoutingKey());
        });

        /**
         * 消费端确认 (只有每个消息被正确消费了 broker才可以删除这个消息)
         * 1、rabbitmq默认是自动应答的,只要消息接收到,客户端会自动确认,服务端就会移除这个消息
         *      问题:在debug模式 10个我们接收到一个消息后如果发生服务宕机 那剩下的消息会全部丢失
         *      解决:手动确认 :只要没有告诉mq消息被处理了 没有被ack 那消息是不会被删除的
         *                   即使consumer宕机了 也不会丢失消息 会将消息变为ready状态
         *                   当下一次还有链接进来的时候 发送给他
         *          1:如何进行手动确认
         */
    }
}
